/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.nodes; import java.util.ArrayList; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; import java.util.Comparator; import java.util.Iterator; import java.util.Enumeration; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import org.openide.util.enum.ArrayEnumeration; import org.openide.util.actions.SystemAction; import org.openide.util.actions.Presenter; import org.openide.awt.JPopupMenuPlus; /** Utility class for operations on nodes. * * @author Jaroslav Tulach, Petr Hamernik, Dafe Simonek */ public final class NodeOp extends Object { private NodeOp() {} /** default node actions */ private static SystemAction[] defaultActions; /** Set the default actions for all nodes. * @param def array of default actions * @exception SecurityException when the default actions have already been set */ public static synchronized void setDefaultActions (SystemAction[] def) { if (defaultActions != null) throw new SecurityException (); defaultActions = def; } /** Get the default actions for all nodes. * @return array of default actions */ public static SystemAction[] getDefaultActions () { return defaultActions == null ? new SystemAction[0] : defaultActions; } /** Compute common menu for specified nodes. * Provides only those actions supplied by all nodes in the list. * @param nodes the nodes * @return the menu for all nodes */ public static JPopupMenu findContextMenu (Node[] nodes) { if (nodes.length != 1) return findContextMenuImpl(nodes); else return nodes[0].getContextMenu(); } /** Method for finding popup menu for one or more nodes. * * @param nodes array of nodes * @return popup menu for this array */ static JPopupMenu findContextMenuImpl (Node[] nodes) { JPopupMenu menu = new JPopupMenuPlus (); // hashtable: SystemAction -> Integer HashMap actions = new HashMap (); // counts the number of occurences for each action for (int n = 0; n < nodes.length; n++) { SystemAction[] arr = nodes[n].getActions (); if (arr == null) { // use default actions arr = defaultActions; } for (int i = 0; i < arr.length; i++) { if (arr[i] != null) { Integer cntInt = (Integer)actions.get (arr[i]); int cnt = cntInt == null ? 0 : cntInt.intValue (); actions.put (arr[i], new Integer (cnt + 1)); } } } // take all actions that are nodes.length number times if (!actions.isEmpty ()) { SystemAction[] arr = nodes[0].getActions (); if (arr == null) { // use default arr = defaultActions; } boolean canSep = false; for (int i = 0; i < arr.length; i++) { boolean addSep = true; if (arr[i] != null) { Integer cntInt = (Integer)actions.get (arr[i]); int cnt = cntInt == null ? 0 : cntInt.intValue (); if (cnt == nodes.length) { addSep = false; canSep = true; JMenuItem item; if (arr[i] instanceof Presenter.Popup) { item = ((Presenter.Popup)arr[i]).getPopupPresenter (); } else { item = new JMenuItem (arr[i].getName ()); item.setEnabled (false); } menu.add (item); } } if (addSep && canSep) { menu.addSeparator (); canSep = false; } } } return menu; } /** Test whether the second node is a (direct) child of the first one. * @param parent parent node * @param son son node * @return <code>true</code> if so */ public static boolean isSon (Node parent, Node son) { return son.getParentNode () == parent; } /** Find a path (by name) from one node to the root or a parent. * @param node the node to start in * @param parent parent node to stop in (can be <code>null</code> for the root) * @return list of child names--i.e. a path from the parent to the child node */ public static String[] createPath (Node node, Node parent) { LinkedList ar = new LinkedList (); while (node != null && node != parent) { if (node.getName() == null) throw new NullPointerException ("" + node.getClass () + " : " + node.getDisplayName ()); // NOI18N ar.addFirst (node.getName ()); node = node.getParentNode (); } String[] res = new String [ar.size ()]; ar.toArray (res); return res; } /** Look for a node child of given name. * @param node node to search in * @param name name of child to look for * @return the found child, or <code>null</code> if there is no such child */ public static Node findChild (Node node, String name) { return node.getChildren ().findChild (name); } /** Traverse a path from a parent node down, by an enumeration of names. * @param start node to start searching at * @param names enumeration of <code>String</code>s containing names of nodes * along the path * @return the node with such a path from the start node * @exception NodeNotFoundException if the node with such name * does not exists; the exception contains additional information * about the failure. */ public static Node findPath (Node start, Enumeration names) throws NodeNotFoundException { int depth = 0; while (names.hasMoreElements ()) { String name = (String)names.nextElement (); Node next = findChild (start, name); if (next == null) { // no element in list matched the name => fail // fire exception with the last accessed node and the // name of child that does not exists throw new NodeNotFoundException (start, name, depth); } else { // go on next node start = next; } // continue on next depth depth++; } return start; } /** Traverse a path from a parent node down, by an enumeration of names. * @param start node to start searching at * @param names names of nodes * along the path * @return the node with such a path from the start node * @exception NodeNotFoundException if the node with such name * does not exists; the exception contains additional information * about the failure. */ public static Node findPath (Node start, String[] names) throws NodeNotFoundException { return findPath (start, new ArrayEnumeration (names)); } /** Find the root for a given node. * @param node the node * @return its root */ public static Node findRoot (Node node) { for (;;) { Node parent = node.getParentNode (); if (parent == null) return node; node = parent; } } /** Package private utility method, support for sorting nodes * in children in natural ordering (without comparator). * Computes the permutation how to sort children. * @return permutation to use on children to achieve the right * ordering */ final static int[] computeSortingPermutation (Node[] nodes) { return computeSortingPermutation(nodes, null); } /** Package private utility method. Support for sorting nodes * in children. * Computes the permutation how to sort children with given comparator. * @return permutation to use on children to achieve the right * ordering */ final static int[] computeSortingPermutation (Node[] nodes, Comparator comp) { // creates sorted map that assignes to nodes their original // position TreeMap tm = comp == null ? new TreeMap () : new TreeMap (comp); for (int i = 0; i < nodes.length; i++) { tm.put (nodes[i], new Integer (i)); } // takes nodes one by one in the new order and // creates permutation array int[] perm = new int[nodes.length]; Iterator it = tm.entrySet ().iterator (); for (int i = 0; i < perm.length; i++) { Map.Entry entry = (Map.Entry)it.next (); // old position of the node that is now on i-th position int oldPos = ((Integer)entry.getValue ()).intValue (); // perm must move the object on the oldPos to new position i perm[oldPos] = i; } return perm; } /** Compute a permutation between two arrays of nodes. The arrays * must have the same size. The permutation then can be * applied to the first array to create the * second array. * * @param arr1 first array * @param arr2 second array * @return the permutation, or <code>null</code> if the arrays are the same * @exception IllegalArgumentException if the arrays cannot be permuted to each other. Either * they have different sizes or they do not contain the same elements. * @see Children#reorder */ public static int[] computePermutation (Node[] arr1, Node[] arr2) throws IllegalArgumentException { if (arr1.length != arr2.length) { int max = Math.max (arr1.length, arr2.length); StringBuffer sb = new StringBuffer (); for (int i = 0; i < max; i++) { sb.append (i + " "); // NOI18N if (i < arr1.length) { sb.append (arr1[i].getName ()); } else { sb.append ("---"); // NOI18N } sb.append (" = "); // NOI18N if (i < arr2.length) { sb.append (arr2[i].getName ()); } else { sb.append ("---"); // NOI18N } sb.append ('\n'); } throw new IllegalArgumentException (sb.toString ()); } // creates map that assignes to nodes their original // position HashMap map = new HashMap (); for (int i = 0; i < arr2.length; i++) { map.put (arr2[i], new Integer (i)); } // takes nodes one by one in the new order and // creates permutation array int[] perm = new int[arr1.length]; int diff = 0; for (int i = 0; i < arr1.length; i++) { // get the position of the i-th argument in the second array Integer newPos = (Integer)map.get (arr1[i]); if (newPos == null) { // not permutation i-th element is missing in the array throw new IllegalArgumentException ("Missing permutation index " + i); // NOI18N } // perm must move the object to the newPos perm[i] = newPos.intValue (); if (perm[i] != i) { diff++; } } return diff == 0 ? null : perm; } } /* * Log * 16 Gandalf 1.15 3/11/00 Martin Ryzl menufix [by E.Adams, * I.Formanek] * 15 Gandalf 1.14 1/12/00 Jesse Glick NOI18N * 14 Gandalf 1.13 1/5/00 Jaroslav Tulach Newly created objects * are selected in explorer * 13 Gandalf 1.12 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 12 Gandalf 1.11 8/27/99 Jaroslav Tulach New threading model & * Children. * 11 Gandalf 1.10 8/3/99 Jan Jancura * 10 Gandalf 1.9 8/2/99 Ian Formanek Removed comment * 9 Gandalf 1.8 7/30/99 David Simonek serialization fixes * 8 Gandalf 1.7 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 7 Gandalf 1.6 5/25/99 Jaroslav Tulach Children.Keys.setKeys * now clones the collection, so no later modification matter * 6 Gandalf 1.5 5/25/99 Jaroslav Tulach Fix #1889 * 5 Gandalf 1.4 4/15/99 Jaroslav Tulach Faster * computeSortingPerm. * 4 Gandalf 1.3 3/17/99 Jesse Glick [JavaDoc] * 3 Gandalf 1.2 1/11/99 Jan Jancura * 2 Gandalf 1.1 1/6/99 Ales Novak * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ * Beta Change History: * 0 Tuborg 0.12 --/--/98 Jan Formanek getMenuPresenter replaced by getPopupPresenter * 0 Tuborg 0.13 --/--/98 Jaroslav Tulach isSon */